<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        /* 
    如何判断函数中的this?
    1. 常规的情况: 看调用方法
        直接调用  fn()  => window/undefined
        new调用   new fn() => 新创建的实例
        对象调用   obj.fn()  => obj
        call/apply/bind()调用:  => 第一个参数对象
    2. 特殊情况
        箭头函数  => 沿着作用域链去外部查找this
    */

        //自定义call
        Function.prototype.call = function (thisObj, ...args) {
            console.log('call')

            //处理thisObj的特殊值
            if (thisObj == null || thisObj == undefined) {
                thisObj = window
            }
            // 给thisObj添加一个临时方法, 值为当前函数this
            thisObj.tempFn=this
            // 通过thisObj来调用方法 ==> 执行的就是当前函数   得到执行结果
            const result = thisObj.tempFn(...args);
            // 删除这个方法
            delete thisObj.tempFn
            //返回执行结果
            return result 
        }

        //自定义apply
        Function.prototype.apply = function(thisObj,args){
            return this.call(thisObj,...args)
        }


        //自定义bind
        // 返回一个新函数, 新函数内部会调用原函数, 且this为bind指定的参数对象
        Function.prototype.bind = function(thisObj,...args){
            console.log('bind',thisObj,args);
            //返回一个新函数
            // 新函数内部会调用原函数, 且this为bind指定的参数对象
            return (...args2) => this.call(thisObj,...args,...args2)     
        } 
    </script>
    <script>
        function fn1(){
            console.log(1,this)
        }
        function fn2(){
            console.log(2,this)
        }
        // fn1.call(fn2)    /*    1   fn2(){console.log(2,this)} */
        console.log(fn1.call)
        // call函数来调用call方法
        fn1.call.call(fn2) // 2 window

    </script>
    <script>
        function fn(a,b){
            console.log(a,b,this)
            return a+b
        }
        const obj={m:1}
        // 立即执行函数, 并指定内部的this为第一个参数
        // console.log(fn.call(obj,2,3))
        // console.log(fn.call(null,2,3))
        // console.log(fn.call(unde,3))

        // bind: 绑定this和参数
        // 返回一个新函数, 新函数内部会调用原函数, 且this为bind指定的参数对象
        // fn.bind(obj)(4,5)//bind {m: 1} []   4 5 {m: 1, tempFn: ƒ}
        // fn.bind(obj,6)(4,5) // bind {m: 1} [6]   6 4 {m: 1, tempFn: ƒ}
        fn.bind(obj,6,7)(4,5) // bind {m: 1} (2) [6, 7]     6 7 {m: 1, tempFn: ƒ}
    </script>
</body>

</html>